////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_INSTRUCTION_H__
#define __WN_INSTRUCTION_H__

#include "WNScripting/inc/WNParseNode.h"

#include <deque>

enum WNInstructionType {
    eWNInstructionNull,
    eWNInstructionAssign,
    eWNInstructionList,
    eWNInstructionDeclaration,
    eWNInstructionReturn,
    eWNInstructionConditional,
    eWNInstructionConditionalElse,
    eWNInstructionWhileLoop,
    eWNInstructionDoLoop,
    eWNInstructionForLoop,
    eWNInstructionDelete,
    eWNInstructionExpression
};

class WNExpression;
class WNInstructionList;
class WNDataType;
class WNIdentifier;
class WNLValue;

class WNInstruction : public WNParseNode {
public:
    virtual ~WNInstruction();
    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar);

    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNInstructionList* _instructionList);
    
    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNLValue* _lval,
                  WNExpression* _expression);
   
    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNDataType* _type,
                  WNIdentifierElement _identifier,
                  WNLValue* _lvalue,
                  WNExpression* _expression);
    
    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNLValue* _lvalue,
                  WN_UINT32 _deleteLevels);

    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNExpression* _expression,
                  bool _functionCall);

    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNExpression* _expression,
                  WNInstruction* _instruction,
                  WNInstructionType _type);

    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNExpression* _expression,
                  WNInstruction* _instruction,
                  WNInstruction* _elseInstruction);

    WNInstruction(WN_SIZE_T _startLine,
                  WN_SIZE_T _endLine,
                  WN_SIZE_T _startChar,
                  WN_SIZE_T _endChar,
                  WNInstruction* _initial,
                  WNExpression* _condition,
                  WNInstruction* _loopval,
                  WNInstruction* _loopIntern);

    virtual llvm::Value* OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl);
    virtual WN_BOOL ReportErrors();
    WN_BOOL Returns();

private:
    WNInstructionType mType;

    union {
        WNInstructionList* mInstructionList;
        WNIdentifierElement mIdentifier;
        WNInstruction* mInstruction;
        WNLValue* mLvalue;
    } mFirstElement;

    union {
        WNExpression* expression;
        WNDataType* type;
        WN_UINT32 number;
    } mSecondElement;

    union {
        WNExpression* expression;
        WNInstruction* mInstruction;
    } mThirdElement;

    union {
        WNInstruction* mInstruction;
        WNLValue* mLvalue;
    } mFourthElement;

    std::deque<WNInstruction*> mInstructions;
    WN_BOOL mReturns;
    WN_BOOL mHeapAlloc;
};

class WNInstructionList : public WNParseNode {
public:
    virtual ~WNInstructionList();

    WNInstructionList(WN_SIZE_T startLine,
                      WN_SIZE_T endLine,
                      WN_SIZE_T startChar,
                      WN_SIZE_T endChar);

    WNInstructionList(WN_SIZE_T startLine,
                      WN_SIZE_T endLine,
                      WN_SIZE_T startChar,
                      WN_SIZE_T endChar,
                      WNInstruction* instruction,
                      WNInstructionList* instructionList);

    WNInstruction* AppropriateInstruction() { WNInstruction* elem = mInstruction; mInstruction = WN_NULL; return(elem); }
    WNInstructionList* AppropriateInstructionList() { WNInstructionList * elem = mInstructionList; mInstructionList = WN_NULL; return(elem); }
    virtual llvm::Value* OptimizeAndGenerate(WNScriptingEngineData& scriptingData, WNFunctionDecl* functionDecl);
    virtual WN_BOOL ReportErrors();
    WN_BOOL Returns();

private:
    WNInstruction* mInstruction;
    WNInstructionList* mInstructionList;
    WN_BOOL mReturns;
};

#endif//__WN_INSTRUCTION_H__